COMP3141 Software System Design and Implementation

COMP3141: Software System Design and Implementation

Term 2, 2023

Code and Notes (Week 7 Wednesday)

Table of Contents

1 Live code

This is all the code I wrote during the lecture. No guarantee that it makes any sense out of context.

module Week7 where

import Control.Monad
import Test.QuickCheck
import Control.Monad.State
import MoveGenerator

{-
   *
   * -> *
   * -> * -> *
   * -> * -> * -> *
 -}


bindM :: Maybe a -> (a -> Maybe b) -> Maybe b
bindM Nothing k = Nothing
bindM (Just a) k = k a

assert :: Bool -> Maybe ()
assert True = Just ()
assert False = Nothing


{-
return :: Monad m => a -> m       a
yield  ::            a -> State s a
Just   ::            a -> Maybe   a

(>>=) :: Monad m => m       a -> (a -> m       b) -> m       b
bindM ::            Maybe   a -> (a -> Maybe   b) -> Maybe   b
bindS ::            State s a -> (a -> State s b) -> State s b

 -}

{-  Monoid had an associativity law:

       (a <> b) <> c == a <> (b <> c)

       (<>) :: Semigroup a =>         a ->               a ->          a

       (>>=) :: Monad m    => m       a -> (a -> m       b) -> m       b

    Monads also have an associativity law

    You might want to write:

       m >>= (k1 >>= k2)   == (m >>= k1) >>= k2     but the types don't match :(

       (m >>= \x -> k1 x) >>= \y -> k2 y
       ==
       m >>= (\x -> (k1 x >>= \y -> k2 y))

    ^ Morally, this is still an associativity law:
      its main message is that how we bracket the composition of multiple binds doesn't matter


    This law can be simplified by using eta-contraction
             (\x -> f x)  ==   f         (eta conversion)

    (m >>= k1) >>= k2  ==  m >>= (\x -> (k1 x >>= k2))



    something >>= \x -> do_something x >>= \y -> do_something_else x y

    something >>= (\x -> do_something >>= (\y -> do_something_else))


  Monoid identity laws:

    mempty `mappend` x   == x    (left identity)

    x `mappend` mempty   == x    (right identity)

  Morally, the monad identity laws are the same thing:

    they tell us that putting return to the left or right
    of >>=   does nothing.
    But they types don't line up as neatly as for monoids, so more lambdas :(
 -}


{-

   m a        <-- monad action
   a -> m b   <-- function that returns a monad action

   Functions that return monad actions are called
    *Kleisli arrows*

   (>>=) :: m a           ->      (a -> m b)         ->    m b
            ^monad action         ^kleisli arrow           ^ monad action

   We're going to define something called *Kleisli composition*
   Kleisli composition is to bind
    as function composition is to function application


    f          .     g
    ^function        ^function        return function

    f           (x)

    ^function   ^value                return value

    Klesli composition   <=<


    k1             <=<  k1
    ^Kleisli arrow      ^Kleisli arrow        return Kleisli arrow


    (<=<)  :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)

    (.) ::               (b ->   c) -> (a ->   b) -> (a ->   c)
 -}

incrementTwice :: State Int Bool
incrementTwice =
  modify succ >>= \_ -> modify succ >>= \_ -> get >>= \x -> return(even x)

{-
 modify :: (s -> s) -> State s ()
-}

incrementTwice' :: State Int Bool
incrementTwice' =
  do -- nothing to do with loops
    modify succ --a bind is inserted between every newline
    modify succ
    x <- get
    return(even x) -- nothing to do with returning from functions


{-

   return  :: a -> [a]
   return = flip (:[])

   return x = [x]



   (>>=)   :: [a] -> (a -> [b]) -> [b]

   xs >>= f  = concat(map f xs)
 -}

{- The list of all possible outcomes of rolling
   a six-sided die
 -}
d6 :: [Int]
d6 = [1..6]

twod6plus3 :: [Int]
twod6plus3 =
  d6 >>= \x -> d6 >>= \y -> return $ x + y + 3

twod6plus3' :: [Int]
twod6plus3' =
  do
    x <- d6
    y <- d6
    return $ x + y + 3

{- The list monad is good for exploring branching trees
   of possibilities


                    d2
                 1 /  \  2
                  /    \
                 d2     d2
                / \     |  \
             1 /   \ 2  |1  \ 2

    what the list monad does is flatten these trees
    into a list of possible outcomes for the whole process


    Whenever you want to sequence actions that have a list
    of possible outcomes, the list monad is the thing for you.
-}

{- Backtracking search


    hel
    hell
    hello
    (ignoring words of length 1, every prefix of hello is also word (in some dictionary))
 -}

{- Given a stem  "he"
   what are all the possible extensions of "he" that add 3 letters?
 -}
extension :: (String -> Bool) -> String -> Int -> [String]
extension d w 0 = return w
extension d w n =
  do
    c <- ['a'..'z']
    if d (w++[c]) then
      extension d (w++[c]) (n-1)
    else
      []

{- If we have no stem
   what are all the possible words of length n such that all
   prefixes of length>=2 are words
 -}
niceWords :: (String -> Bool) -> Int -> [String]
niceWords d n
  | n < 2 = []
  | otherwise =
    do
      x <- ['a'..'z']
      y <- ['a'..'z']
      if d [x,y] then
        extension d [x,y] (n-2)
      else
        []

{-  The list monoid:

       [a]    is a monoid     for all a

       ^ monoid operation is (++) append

    The list monad:

       []     is a monad

       ^ monad operation is concatMap

       concatMap f = concat . map f

       concat = mconcat

    Difference fmap   and  map f


    fmap is functor map

    Functor f  => (a -> b) -> f a -> f b

                  (a -> b) -> [a] -> [b]

    map is just fmap specialised to lists
     (where the type f is [])
 -}

2 ScoMo live code

This is the live code I wrote for a bonus video.

module Week7 where

import Control.Monad
import Test.QuickCheck
import Control.Monad.State

{- f :: * -> *

   is Applicative if it has

      pure :: a -> f a
      (<*>) :: f(a -> b) -> f(a) -> f(b)
 -}

{-
    Functor f
       fmap :: (a -> b) -> f a -> f b

    Applicative f
       pure :: a -> f a
       (<*>) :: f(a -> b) -> f(a) -> f(b)

    Monad
       return :: a -> m a
       (>>=)  :: m a -> (a -> m b) -> m b

    Every Monad is also Applicative,
     and every Applicative is also a Functor
 -}

class MyFunctor f where
  myFmap :: (a -> b) -> f a -> f b

class MyFunctor f => MyApplicative f where
  myPure :: a -> f a
  myApp  :: f(a -> b) -> f a -> f b

class MyApplicative m => MyMonad m where
  myReturn  :: a -> m a
  myBind    :: m a -> (a -> m b) -> m b

instance MyFunctor Maybe where
  myFmap f Nothing = Nothing
  myFmap f (Just x) = Just(f x)

instance MyApplicative Maybe where
  myPure = Just
  myApp (Just f) (Just x) = Just(f x)
  myApp _ _ = Nothing

instance MyMonad Maybe where
  myReturn = myPure -- You'll always want to define return = pure
                    -- the fact that return can be defined separate from pure
                    -- is just historical accident
  myBind m k =
    case m of
      Nothing -> Nothing
      Just a -> k a

{- ScoMo Monad

   Sometimes, it's useful to parametise a computation on an inexhaustible source
   of Scott Morrison quotes.

        >>= :: ...
     return :: a -> ScoMo a
      quote :: ScoMo String  -- a computation that returns a Scott Morrison quote
 -}

quotes :: [String]
quotes = cycle
  ["It’s like that movie The Croods",
   "The sooner we get there, the sooner we get there",
   "I don’t hold a hose, mate",
   "If you have a go, you get a go",
   "It’s not a race",
   "Not far from here, such marches, even now are being met with bullets",
   "I don’t accept the premise of your question",
   "Jenny has a way of clarifying things"]

-- The Int argument is an offset into a hard-coded list of quotes.
data ScoMo a = ScoMo (Int -> (Int,a))

quote :: ScoMo String
quote = ScoMo (\n -> (n+1,quotes !! n))

instance Functor ScoMo where
  fmap f (ScoMo sc) =
    ScoMo $ (f <$>) . sc

instance Applicative ScoMo where
  pure a = ScoMo (\s -> (s,a))
  ScoMo scf <*> ScoMo sca =
    ScoMo $ \s ->
      let (s',f) = scf s
          (s'',a) = sca s'
      in (s'',f a)

instance Monad ScoMo where
  return = pure
  ScoMo scm >>= k =
    ScoMo $ \s ->
      let (s',a) = scm s
          ScoMo scm' = k a
      in scm' s'

impartWisdom :: ScoMo String
impartWisdom = do
  xs <- quote
  ys <- quote
  return $ concat
    ["I wise man once said: ",xs,"\n",
     "I wise man also once said: ",ys,"\n"
    ]

evalScoMo :: ScoMo a -> a
evalScoMo (ScoMo smc) =
  snd(smc 0)

{-
  fmap f (Scomo sc) =
    ScoMo $ \s ->
      let (s',r) = sc s
      in (s',f r)
 -}

{- I claimed that:

     every Applicative is a Functor

     every Monad is Applicative

     To justify this claim, it should be possible to
     define the Applicative operators
     interms of Monad operators,
     and the Functor operator in terms of the
     Applicative operators.


       pure ::  a -> f a
       (<*>) :: f (a -> b) -> f a -> f b

       fmap :: (a -> b) -> f a -> f b
       fmap f xs = pure f <*> xs


       return :: a -> m a
       (>>=) :: m a -> (a -> m b) -> m b

       pure ::  a -> m a
       pure = return
       (<*>) :: m (a -> b) -> m a -> m b
       mab <*> ma =
         do
           f <- mab
           a <- ma
           return $ f a

    ^ this works, but we'd need to check the laws (which laws?)



   -- Identity
   pure id <*> v = v
   -- Homomorphism
   pure f <*> pure x = pure (f x)
   -- Interchange
   f <*> pure y = pure (\g -> g y) <*> f
   -- Composition
   pure (.) <*> u <*> v <*> w = u <*> (v <*> w)


   <*> is morally like function application:
       it is function application lifted over
       an applicative functor


   ($)   ::                    (a -> b) ->   a ->   b
   (<*>) :: Applicative f => f (a -> b) -> f a -> f b

   Because of this close relation between <*> and ($),
    we would expect the applicative laws to say that
    <*> behaves like function application.


   Applicative laws                              Function laws
   ____________________________________________________________
   pure id <*> v = v                             id $ v = v
   pure f <*> pure x = pure(f x)                 f  $ x = f x
   f <*> pure y = pure (\g -> g y) <*> f         f  $ y = (\g -> g y) $ f
   pure (.) <*> u <*> v <*> w = u <*> (v <*> w)  (.) u v $ w = u $ (v $ w)
                                                 (u . v) w   = u (v w)

   Summary:
     The applicative laws state that morally,
     <*> behaves like function application.

 -}

2023-08-13 Sun 12:52

Announcements RSS